/**
 * 虚拟滚动列表组件
 * 用于优化长列表性能，只渲染可见区域的元素
 */

import { View, ScrollView } from '@tarojs/components';
import { useState, useEffect, useMemo, useRef } from 'react';
import type { FC, ReactNode } from 'react';
import './index.scss';

interface VirtualListProps<T> {
  /** 数据源 */
  data: T[];
  /** 每项的高度（px） */
  itemHeight: number;
  /** 容器高度（px） */
  height: number;
  /** 渲染每一项的函数 */
  renderItem: (item: T, index: number) => ReactNode;
  /** 缓冲区大小（在可见区域上下额外渲染的项数） */
  bufferSize?: number;
  /** 唯一键提取函数 */
  getKey?: (item: T, index: number) => string | number;
  /** 滚动回调 */
  onScroll?: (scrollTop: number) => void;
  /** className */
  className?: string;
}

function VirtualList<T = any>(props: VirtualListProps<T>) {
  const {
    data,
    itemHeight,
    height,
    renderItem,
    bufferSize = 3,
    getKey = (_, index) => index,
    onScroll,
    className = '',
  } = props;

  const [scrollTop, setScrollTop] = useState(0);

  // 计算可见范围
  const visibleRange = useMemo(() => {
    const start = Math.max(0, Math.floor(scrollTop / itemHeight) - bufferSize);
    const visibleCount = Math.ceil(height / itemHeight);
    const end = Math.min(
      data.length,
      Math.ceil(scrollTop / itemHeight) + visibleCount + bufferSize,
    );

    return { start, end };
  }, [scrollTop, itemHeight, height, data.length, bufferSize]);

  // 可见的数据
  const visibleData = useMemo(() => {
    return data.slice(visibleRange.start, visibleRange.end);
  }, [data, visibleRange]);

  // 总高度
  const totalHeight = data.length * itemHeight;

  // 偏移量
  const offsetY = visibleRange.start * itemHeight;

  const handleScroll = (e: any) => {
    const newScrollTop = e.detail.scrollTop;
    setScrollTop(newScrollTop);
    onScroll?.(newScrollTop);
  };

  return (
    <ScrollView
      className={`virtual-list ${className}`}
      scrollY
      style={{ height: `${height}px` }}
      onScroll={handleScroll}
      scrollTop={scrollTop}
    >
      <View
        className="virtual-list__container"
        style={{ height: `${totalHeight}px` }}
      >
        <View
          className="virtual-list__content"
          style={{ transform: `translateY(${offsetY}px)` }}
        >
          {visibleData.map((item, index) => {
            const actualIndex = visibleRange.start + index;
            return (
              <View
                key={getKey(item, actualIndex)}
                className="virtual-list__item"
                style={{ height: `${itemHeight}px` }}
              >
                {renderItem(item, actualIndex)}
              </View>
            );
          })}
        </View>
      </View>
    </ScrollView>
  );
}

export default VirtualList;
